<script>
  import { setUpdate, update } from '$lib/ui';
  import { onMount } from 'svelte';
  import TileMap from '../TileMap.svelte';
  import { renderTiles } from '$lib/render3';
  import { postProcess, renderStack } from './render';
  import { ensureMission } from './mission';
  import Scrollable from '../Scrollable.svelte';
  import Environment from '../Environment.svelte';
  import SelectedInfo from '../SelectedInfo.svelte';
  import { getPos3, landscapeAt, onTile } from '$lib/pos3';
  import { movementKeys } from '$lib/keyboard';
  import Entities from '../Entities.svelte';
  import { hasComponent } from '$lib/ecs';
  import { Creature, Impassable } from '$lib/components';
  import { HumanMove, humanCanPass } from './human';
  import { simulateWorld } from '$lib/simulate';
  import { log, recentLog } from '$lib/log';
  import Log from '../Log.svelte';
  import { Stairs } from './stairs';
  import { Door, OpenAction } from '$lib/door';
  import CharStatus from './CharStatus.svelte';
  import { Attackable } from './attack';
  import { Interactable } from './interact';
  import Inventory from './Inventory.svelte';
  import { getInventory } from '$lib/inventory';
  import Equipment from './Equipment.svelte';
  import { getEquipped } from './equipment';
  import { isGameOver } from '$lib/clock';
  import { MeleeAttack, meleeAttack } from './melee';

  let { game } = $props();

  let innerWidth = $state();
  let innerHeight = $state();
  let tileMapWidget = $state();
  let logWidget = $state();
  let leftSideInfo = $state();

  const zoomlevel = 32;

  let mapZoom = $state(32);

  let debug = $state(true);
  let tick = $state(false);

  let theLog = $state(['hi']);
  let tileMap = $state([]);
  let interesting = $state({});

  let selected = $state(undefined);
  // TODO: move to lib
  const select = (entity) => {
    if (selected === entity) {
      selected = undefined;
    } else {
      selected = entity;
    }
  };

  let inventory = $derived((tick, getInventory(game.world, game.pc)));
  let equipment = $derived((tick, getEquipped(game.world, game.pc)));

  let inspectTile = $state(undefined);
  const selectInspectTile = (tile) => {
    inspectTile = tile;
  };

  setUpdate(() => {
    if (isGameOver(game.world)) {
      game.over = true;
      game = game;
    } else {
      ensureMission(game.world);
    }
    let underRendered;
    const r = Math.floor((1080 / mapZoom - 1) / 2);
    [underRendered, interesting] = renderTiles(renderStack, game.world, r);
    tileMap = postProcess(underRendered);
    theLog = recentLog();
    tick = !tick;
  });

  onMount(() => {
    update();
    tileMapWidget.focus();
  });

  const onKeyPress = (ev) => {
    console.log(ev);
    if (ev.key === '\\') {
      debug = !debug;
      log(`Debug ${debug ? 'enabled' : 'disabled'}`);
    } else if (ev.key === '(') {
      mapZoom = Math.max(mapZoom-1, 1);
    } else if (ev.key === ')') {
      ++mapZoom;
    }
    update();
  };

  const onKeyPressTiles = (ev) => {
    const world = game.world;
    const pc = game.pc;
    const {x, y, z} = getPos3(pc);
    let cmd;
    const movement = movementKeys[ev.key];
    if (movement !== undefined) {
      const [dx, dy, dz] = movement;
      if (movement.some((d) => d !== 0) && movement[2] === 0) {
        // TODO: move this to lib
        const x1 = x+dx;
        const y1 = y+dy;
        const z1 = z+dz;
        /* const landscape = landscapeAt(world, x1, y1, z1); */
        const things = onTile(world, x1, y1, z1);
        const passable = humanCanPass(world, things);
        const creature = things.find((e) => hasComponent(world, Creature, e));
        const obstacle = things.find((e) => hasComponent(world, Impassable, e));
        if (creature) {
          if (hasComponent(world, Interactable, creature)) {
            log("To be interacted");
          } else if (hasComponent(world, Attackable, creature)) {
            // TODO: check when attacking non-hostile creatures
            cmd = MeleeAttack(creature);
          }
        } else if (hasComponent(world, Door, obstacle)) {
          cmd = OpenAction(obstacle, 10);
        } else if (passable) {
          cmd = HumanMove(dx, dy, dz);
        }
      } else if (movement[2] !== 0) {
        const here = onTile(world, x, y, z);
        if (here.some((e) => hasComponent(world, Stairs, e)
                        && Stairs.direction[e] === movement[2])) {
          log('Trying to climb stairs');
          const z1 = z + movement[2];
          const there = onTile(world, x, y, z1);
          const creature = there.find((e) => hasComponent(world, Creature, e));
          if (creature !== undefined) {
            log("Cannot climb, someone is blocking the way!", 'command-failed-minor');
          } else {
            // assuming there could be no obstacle on stairs other than a creature
            cmd = HumanMove(dx, dy, dz);
            console.log(cmd);
          }
        } else {
          const directionName = movement[2] === 1 ? 'up' : 'down';
          log(`Cannot climb ${directionName} here.`, 'command-invalid-minor');
        }
      }
    }
    if (cmd) {
      simulateWorld(world, cmd);
      selectInspectTile(undefined);
      select(undefined);
    }
    update();
  };
</script>

<svelte:window bind:innerWidth bind:innerHeight on:keypress={onKeyPress} />

<div class="main-grid">
  <Scrollable bind:this={leftSideInfo} height="100vh" scrollToEnd={false} extraClass="leftside">
    <Entities name="─ Things here ─" world={game.world} {select} {selected} bind:stack={interesting.currentTile} {zoomlevel} />
    <Environment name="─ Things around ─" world={game.world} {select} {selected} environment={interesting} {zoomlevel} />
    {#if inspectTile !== undefined}
      <Entities name="─ Selected tile ─" world={game.world} {select} {selected} bind:stack={inspectTile.stack} {zoomlevel} />
    {/if}
    <SelectedInfo world={game.world} {selected} {zoomlevel} {debug} />
  </Scrollable>
  <TileMap bind:this={tileMapWidget} bind:tiles={tileMap} zoomlevel={mapZoom} {inspectTile} {select} {selectInspectTile} onKeyDown={onKeyPressTiles} />
  <div class="rightside">
    <CharStatus world={game.world} char={game.pc} bind:tick />
    <Equipment world={game.world} char={game.pc} {equipment} />
    <Inventory world={game.world} char={game.pc} {inventory} />
    <Scrollable height="50vh" scrollToEnd={true}>
      <Log bind:log={theLog} {zoomlevel} />
    </Scrollable>
  </div>
</div>

<style>
  .main-grid {
    display: grid;
    grid-template-columns: 1fr 100vh 1fr;
    grid-template-areas:
      "leftside main rightside";
  }
</style>
